#!/usr/bin/env python

import sys
import re
import os
import subprocess
import time
import datetime

libs = []
maps = []

def getlibandaddr2line(address, procid):

   global libs
   global maps

   result = 'lib?|function?'
   for map in maps:

      if procid in map:

         #sys.stderr.write('map=%s\n' % map)

         maparea = (re.search(r'[0-9a-f]+-[0-9a-f]+', map)).group()
         mapareas = maparea.split('-')

         #sys.stderr.write('mapareas=%s' % mapareas)

         if int(address, 16) >= int(mapareas[0], 16) and int(address, 16) < int(mapareas[1], 16):

            #sys.stderr.write('%s\n' % address)
            #sys.stderr.write('%d\n' % len(libs))

            for lib in libs:

               #sys.stderr.write('lib=%s\n' % lib)

               if os.path.basename(map.split()[6]) in lib:
                  cmd = 'addr2line -s -f -p -C' + ' -e ' + lib + ' -a ' + hex(int(address, 16) - int(mapareas[0], 16))
                  #sys.stderr.write('cmd=%s\n' % cmd)

                  addr2line = subprocess.check_output(cmd, shell = True).replace('\n', '').split(': ')[1]

                  thelib = os.path.basename(map.split()[5])
                  thefullfunc = addr2line.split(' at ')[0]
                  thefullfunc = thefullfunc.replace(', ', ',')
                  thefullfunc = thefullfunc.replace(' ', '-')
                  thefuncname = thefullfunc.split('(')[0]

                  #result = thefuncname
                  result = str('<%s>%s' % (thelib, thefullfunc))
                  break
            break

   return result

def sortby(x):
   return len(x.split()[0]), x.split()[0].lower(), int(x.split()[1][1:])

def main():

   global libs
   global maps

   addresses = []
   addr2lines = []


   if len(sys.argv) == 3:

      #build maps
      f = open(sys.argv[1])
      #DSC 0 0 MAPS,741b1000-74213000,r-xp,00000000,00:10,56754894,,,/lib/libuClibc-0.9.32.1.so
      for line in f:
         #result = re.match(r'DSC 0 0 MAPS,[0-9a-f]{8}', line)
         result = re.match(r'DSC 0 0 MAPS.*r-xp', line)
         if result:
            l = line[12:].replace(',',' ')
            if l not in maps:
               maps.append(l)
      f.close()
      #for m in maps:
      #   sys.stderr.write('maps=%s' %m)

      #build procids lookup
      procids = [None] * 10000;
      f = open(sys.argv[1])
      #NAM 1 1000 [rpcprocess][14082][0]mmap()
      for line in f:
         if line.startswith('NAM'):
            n = int (line.split()[2])
            s = line.split()[3].split('[0]')[0];
            #sys.stderr.write('procids [%d] = %s\n' %(n, s))
            procids[ n ] = s;
      f.close()
      #n = 0
      #for p in procids:
      #   sys.stderr.write('procids=%d %s\n' %(n, p))
      #   n = n + 1;


      #build libs
      libs = []
      sys.stderr.write('building libs')
      sys.stderr.flush()
      #ts = datetime.datetime.now()
      for root, dirs, files in os.walk(sys.argv[2]):
         for file in files:
            #sys.stderr.write('check %s\n' % file)

            if '/' + file + '\\n' in str(maps):
               if 'MIPS' in subprocess.check_output('file ' + root + '/' + file, shell = True):
               #if os.access(fpath, os.X_OK)
               #if os.path.isfile(root + '/' + file):
                  if not root + '/' + file in libs:
                     libs.append(root + '/' + file)
                     sys.stderr.write('.')
                     sys.stderr.flush()
      #tf = datetime.datetime.now()
      #sys.stderr.write('libs done in %s\n' % str(tf - ts))

      sys.stderr.write('\n')
      for l in libs:
         sys.stderr.write('%s\n' %l)

      f = open(sys.argv[1])
      sys.stderr.write('labeling return addresses...')
      sys.stderr.flush()

      id = 0;
      for line in f:

         if line.startswith('STA') or line.startswith('STO') or line.startswith('OCC'):
            id = int(line.split(' ')[2]);

         if line.startswith('DSC'):

            #result = re.search(r'ra=[0-9a-f]+', line)
            result = re.search(r'@0=[0-9a-f]+', line)
            if result:

               address = (result.group().split(',')[0])[3:]
               if not address in addresses:
                  addresses.append(address)
                  addr2line = getlibandaddr2line(address, procids[id])

                  #sys.stderr.write('%s\n' % addr2line)

                  addr2lines.append(addr2line)

               theaddr2line = addr2lines[addresses.index(address)]
               #sys.stderr.write('%s\n' %theaddr2line)

               #DSC 0 0 QImageData::~QImageData()_free,=b17b78,ra=752ba544,sz=0
               line = line[0:8] + theaddr2line + ':' + line[8:]

               #index = line.find(address) + len(address)
               #line = line[:index] + '{' + theaddr2line + '}' + line[index:]


         print line,

      f.close()
      sys.stderr.write('\n')

   else:

      sys.stderr.write("Usage: %s tdifile pathtorootfs\n" % sys.argv[0])

if __name__ == "__main__":
   main()
